# include "TDAClienteCorreo.h"
# include "TDACorreo.h"
# include "Cola.h"
# include "logdebug.h"
# include "TDAServidorInseguro.h"

# include <string.h>
# include <malloc.h>
# include <time.h>

/* Tamano maximo de la clave de encriptacion */
#define MAX_KEY_LEN 32

/* Limites del mapa de caracteres ASQUI*/
#define MIM_CARACTER_ASQUI 32
#define MAX_CARACTER_ASQUI_KEY_GEN 89
#define MAX_CARACTER_ASQUI 122

/* Comienzo de funciones auxiliares */

/*
 * Genera una clave aleatoria para usar en la encriptacion
 *
 * Parametros:
 * clave	un char* que apunta a un buffer donde se copiara la clave generada
 *
 * Valor de retorno:
 * N/A
 *
 * PRE: clave apunta a una zona de memoria valida con espacio suficiente para contener MAX_KEY_LEN caracteres y ademas el caracter terminador de cadena
 * POST: se ha generado clave para encriptacion y se la ha copiado en clave
 *
 */
void generarClaveRandom(char* clave){
    int lugar,i;
    logmacro("%s\n","Ejecutando generarClaveRandom(..)");
    for (i=0;i<=(MAX_KEY_LEN - 1);i++){
        lugar = rand() % (MAX_CARACTER_ASQUI_KEY_GEN) + MIM_CARACTER_ASQUI;
        clave[i] = lugar;
    }
    logmacro(" Clave generada: %s\n",clave);
}

/*
 * Encripta un correo
 * NOTA: se encripta solamente el cuerpo del correo
 *
 * Parametros:
 * correoLegible	un correo valido a ser encriptado
 * correoEncriptado	un puntero a una zona de memoria valida con espacio suficiente para contener un correo
 *
 * Valor de retorno:
 * N/A
 *
 * PRE: correoLegible es un correo valido
 * 		correoEncriptado apunta a una zona de memoria valida con especio suficiente para contener un correo
 * POST: se ha encriptado el correo provisto y la version encriptada puede obtenerse a partir del parametro correoEncriptado indicado
 *
 */
void encriptar(TDACorreo correoLegible, TDACorreo* correoEncriptado, char* clave){
    logmacro("%s\n","Ejecutando encriptar(..)");
    char* texto = (char*) malloc (sizeof(char)*(MAX_BODY_SIZE+1));
    getTextoCuerpo(correoLegible,texto);
    int i,j=-1,aumento;
    for (i=0; i<strlen(texto); i++, j++){
        if (j>=strlen(clave)) j=0;
        aumento = clave[j];
        if (texto[i]+aumento>MAX_CARACTER_ASQUI) {
            texto[i]=texto[i]+aumento - MAX_CARACTER_ASQUI + MIM_CARACTER_ASQUI + 1;
        } else {
            if (texto[i]+aumento < MIM_CARACTER_ASQUI) {
                logmacro(" Falta %d\n",texto[i]+aumento);
            }
            texto[i]=texto[i]+aumento;
        }
    }
    /*memcpy(correoEncriptado,&correoLegible,sizeof(TDACorreo));*/
    setTextoCuerpo(correoEncriptado,texto);
    getRemitente(correoLegible,texto);
    setRemitente(correoEncriptado,texto);
    setFechaHoraEnviado(correoEncriptado,getFechaHoraEnviado(correoLegible));
    getAsunto(correoLegible,texto);
    setAsunto(correoEncriptado,texto);
    TListaSimple listaDeDestinatarios;
    ls_Crear(&listaDeDestinatarios,sizeof(char)*(MAX_ADDR_SIZE+1));
    getDestinatarios(correoLegible,&listaDeDestinatarios);
    ls_MoverCorriente(&listaDeDestinatarios,LS_PRIMERO);
    do {
        ls_ElemCorriente(listaDeDestinatarios,texto);
        ls_Insertar(&(correoEncriptado->destinatarios),LS_SIGUIENTE,texto);
    } while (ls_MoverCorriente(&listaDeDestinatarios,LS_SIGUIENTE));
    ls_Vaciar(&listaDeDestinatarios);
    free(texto);
}

/*
 * Desencripta un correo
 *
 * Parametros:
 * correoEncriptado	un correo valido a ser desencriptado
 * correoLegible	un puntero a una zona de memoria valida con espacio suficiente para contener un correo
 *
 * Valor de retorno:
 * N/A
 *
 * PRE: correoEncriptado es un correo valido
 * 		correoLegible apunta a una zona de memoria valida con especio suficiente para contener un correo
 * POST: se ha desencriptado el correo provisto y la version desencriptada puede obtenerse a partir del parametro correoLegible indicado
 *
 */
void desencriptar(TDACorreo correoEncriptado, TDACorreo* correoLegible, char* clave){
    logmacro("%s\n","Ejecutando desencriptar(..)");
    int i, aumento, j = 0;
    char* texto = (char*) malloc (sizeof(char)*(MAX_BODY_SIZE+1));
    getTextoCuerpo(correoEncriptado,texto);
    for (i=0; i<strlen(texto); i++, j++){
        if (j>=strlen(clave)) j=0;
        aumento = clave[j];
        if (texto[i]-aumento<MIM_CARACTER_ASQUI) {
            texto[i]=texto[i] - aumento + (122 - MIM_CARACTER_ASQUI + 1);
        } else
            texto[i]=texto[i] - aumento;
    }
    /*memcpy(correoLegible,&correoEncriptado,sizeof(TDACorreo));*/
    setTextoCuerpo(correoLegible,texto);
    getRemitente(correoEncriptado,texto);
    setRemitente(correoLegible,texto);
    setFechaHoraEnviado(correoLegible,getFechaHoraEnviado(correoEncriptado));
    getAsunto(correoEncriptado,texto);
    setAsunto(correoLegible,texto);
    TListaSimple listaDeDestinatarios;
    ls_Crear(&listaDeDestinatarios,sizeof(char)*(MAX_ADDR_SIZE+1));
    getDestinatarios(correoEncriptado,&listaDeDestinatarios);
    ls_MoverCorriente(&listaDeDestinatarios,LS_PRIMERO);
    do {
        ls_ElemCorriente(listaDeDestinatarios,texto);
        ls_Insertar(&(correoLegible->destinatarios),LS_SIGUIENTE,texto);
    } while (ls_MoverCorriente(&listaDeDestinatarios,LS_SIGUIENTE));
    ls_Vaciar(&listaDeDestinatarios);
    free(texto);
}

/* Fin de funciones auxiliares */

void crearClienteCorreo(TDAClienteCorreo* clienteCorreo, char* direcUsr) {
    logmacro("%s\n","Ejecutando crearClienteCorreo(..)")
    clienteCorreo->direcUser = (char*) malloc (sizeof(char)*(MAX_ADDR_SIZE+1));
    clienteCorreo->servidorConectado = NULL;
    clienteCorreo->claveEnvio = (char*) malloc (sizeof(char)*(MAX_CONTENT_LEN+1));
    clienteCorreo->claveRecepcion = (char*) malloc (sizeof(char)*(MAX_CONTENT_LEN+1));

    strcpy(clienteCorreo->direcUser,direcUsr);
    strcpy(clienteCorreo->claveEnvio,"\0");
    strcpy(clienteCorreo->claveRecepcion,"\0");

    generarClaveRandom(clienteCorreo->claveEnvio);
    logmacro(" El cliente de correo tiene como usuario a: %s\n",clienteCorreo->direcUser);
}

void destruirClienteCorreo(TDAClienteCorreo* clienteCorreo) {
    logmacro("%s\n","Ejecutando destruirClienteCorreo(..)");
    strcpy(clienteCorreo->direcUser,"\0");
    strcpy(clienteCorreo->claveEnvio,"\0");
    strcpy(clienteCorreo->claveRecepcion,"\0");
    clienteCorreo->servidorConectado = NULL;

    free(clienteCorreo->direcUser);
    free(clienteCorreo->claveEnvio);
    free(clienteCorreo->claveRecepcion);
}

void getDirecUsr(TDAClienteCorreo clienteCorreo, char* direcUsr) {
    logmacro("%s\n","Ejecutando getDirecUsr(..)");
	strcpy(direcUsr,clienteCorreo.direcUser);
	logmacro(" Se ha obtenido direcUsr: %s\n",clienteCorreo.direcUser);
}

void enviarClave(TDAClienteCorreo clienteCorreo, TDACanalSeguro* canalSeguro) {
    setContenido(canalSeguro, clienteCorreo.claveEnvio);
}

void obtenerClave(TDAClienteCorreo* clienteCorreo, TDACanalSeguro canalSeguro) {
    logmacro("%s\n","Ejecutando obtenerClave(..)");
    getContenido(canalSeguro, clienteCorreo->claveRecepcion);
    logmacro(" Clave obtenida: %s\n",clienteCorreo->claveRecepcion);
}

void regenerarClave(TDAClienteCorreo* clienteCorreo) {
    logmacro("%s\n","Ejecutando regenerarClave(..)");
    generarClaveRandom(clienteCorreo->claveEnvio);
}

int conectar(TDAClienteCorreo* clienteCorreo, TDAServidorInseguro* servidorInseguro) {
    logmacro("%s\n","Ejecutando conectar(..)");
    clienteCorreo->servidorConectado = servidorInseguro;
	return 0;
}

int estaConectado(TDAClienteCorreo clienteCorreo) {
    logmacro("%s\n","Ejecutando estaConectado(..)");
	if (clienteCorreo.servidorConectado==NULL) return 0;
	else return 1;
}

void desconectar(TDAClienteCorreo* clienteCorreo) {
    logmacro("%s\n","Ejecutando desconectar(..)");
    clienteCorreo->servidorConectado = NULL;
}

int enviarCorreo(TDAClienteCorreo clienteCorreo, TDACorreo correo) {
    logmacro("%s\n","Ejecutando enviarCorreo(..)");
    setRemitente(&correo,clienteCorreo.direcUser);
    setFechaHoraEnviado(&correo,time(NULL));
    TDACorreo correoEncriptado;
    crearCorreo(&correoEncriptado);
    encriptar(correo,&correoEncriptado,clienteCorreo.claveEnvio);
    agregarCorreo(clienteCorreo.servidorConectado,correoEncriptado);
    /* En vez del memcpy tendria que utilizar la primitiva agregarCorreo del TDAServidorInseguro*/
    /* memcpy(clienteCorreo.conectadoA,&correoEncriptado,sizeof(TDACorreo));*/
    destruirCorreo(&correoEncriptado);
	return 0;
}

int recibirProximoCorreo(TDAClienteCorreo clienteCorreo, TDACorreo* correo) {
    logmacro("%s\n","recibirProximoCorreo(..)");
    /*
    TDACorreo correoEncriptado, correoVacio;
    crearCorreo(&correoEncriptado);
    crearCorreo(&correoVacio);
    */
    /* En vez del memcpy tendria que utilizar la primitiva obtenerProximoCorreo del TDAServidorInseguro*/
    /*
    memcpy(&correoEncriptado,clienteCorreo.conectadoA,sizeof(TDACorreo));
    desencriptar(correoEncriptado,correo,clienteCorreo.claveRecepcion);
    */
    /* Con el siguiente memcpy borro el contenido del servidor inseguro */

    /*
    memcpy(clienteCorreo.conectadoA,&correoVacio,sizeof(TDACorreo));
    destruirCorreo(&correoEncriptado);
    destruirCorreo(&correoVacio);
    */
	return 0;
}

int obtenerTodos(TDAClienteCorreo cliente, TListaSimple* correos){
    logmacro("%s\n","obtenerTodos(..)");
    TCola colaDeCorreos;
    TDACorreo correoEncriptado,correoDesencriptado;
    C_Crear(&colaDeCorreos,sizeof(TDACorreo));
    crearCorreo(&correoEncriptado);
    crearCorreo(&correoDesencriptado);
    obtenerCorreos(cliente.servidorConectado,cliente.direcUser,&colaDeCorreos);
    while (!C_Vacia(colaDeCorreos)){
        C_Sacar(&colaDeCorreos,&correoEncriptado);
        desencriptar(correoEncriptado,&correoDesencriptado,cliente.claveRecepcion);
        ls_Insertar(correos,LS_SIGUIENTE,&correoDesencriptado);
    }
    destruirCorreo(&correoEncriptado);
    destruirCorreo(&correoDesencriptado);
    return 0;
}

